package org.oimwrapper.connectors.common;

import com.thortech.xl.dataobj.*;
import java.util.*;

import org.oimwrapper.connectors.*;

/**
 * This tasks allows for a scheduled task to be run over multiple IT resource instances,
 * and for the delete recon to be run across the entire dataset.   This tasks looks for all IT   
 * resources of the specified type, and uses all of those instances where the disable parameter 
 * on the IT resource is not set to <code>true</code>.<p>  
 *
 * This task expects the following parameters, in addition to the standard
 * parameters in the {@link OIMReconLookupTaskBase} class.
 * <ul>
 * <li><code>ConcurrentSources</code> The number of concurrent sources to recon against.</li>
 * <li><code>ITResourceProperty</code> The task property under which the recon task expects to find the 
 *                                     IT resource name, for example ITResourceName.</li>
 * <li><code>ITResourceType</code> The type of IT resource to search for use by for this tasks.</li>
 * <li><code>ReconClass</code> The Java class name of the recon task that needs to be run over multiple IT 
 *                             resources.</li>
 * <li><code>AppendITResourceKey</code> Setting this to <code>true</code> will append a "." followed by 
 *                                      the IT resource key for each IT resource being processed.  Lookups will
 *                                      be created as needed.</li>
 * </ul>
 */
public class OIMMultipleSourceLookupRecon extends OIMReconLookupTaskBase {
	
	/**
	 * The Map of records to return.
	 */
	private Map records;

	/**
	 * The default constructor.
	 */
	public OIMMultipleSourceLookupRecon() {
		super("OIMWRAPPER.TASK");
	}

	/**
	 * This method is called once per task, and should initialize and open a connection to the target system.
	 * 
	 * @exception Exception
	 */
	public void openConnection() throws Exception {
		logger.debug("Entering multipleSourceLookupRecon.openConnection()");
		logger.debug("Exiting multipleSourceLookupRecon.openConnection()");
	}

	/**
	 * This method is called once per task, and should close the open connection to the target system.
	 *
	 * @exception Exception
	 */
	public void closeConnection() throws Exception {
		logger.debug("Entering multipleSourceLookupRecon.closeConnection()");
		logger.debug("Exiting multipleSourceLookupRecon.closeConnection()");
	}

	/**
	 * This method is called once to retrieve a Map of records on the target system.   The key value of the Map
	 * will be used as the encode value, and the value associated with that key will be used as the decode value;
	 * 
	 * @return A Map of records on the target system.
	 * @exception Exception
	 */
	public Map getRecords() throws Exception {
		logger.debug("Entering multipleSourceLookupRecon.getRecords()");
		records = new HashMap();
		boolean lookupForEach = getBooleanAttribute("AppendITResourceKey", false);
		int maxWorkers = Integer.parseInt(getDefaultAttribute("ConcurrentSources", "10"));
		Map[] itResources = getITResOfType(getCriticalAttribute("ITResourceType"));
		for (int i = 0; !isStopped() && i < itResources.length; i++) {
			String itResource = (String) itResources[i].get("Name");
			long itResourceKey = Long.parseLong((String) itResources[i].get("Key"));
			if (itResources[i].get("Disable") != null && ((String) itResources[i].get("Disable")).equalsIgnoreCase("TRUE")) {  
				logger.debug("Skipping IT Resource " + itResource + "; disable is set to true");  
				continue;  
			}
			logger.debug("Processing IT Resource " + itResource);
			startThread(maxWorkers, new worker(this, itResource, itResourceKey, lookupForEach));
		}
		waitForAllThreads();
		logger.debug("Exiting multipleSourceLookupRecon.getRecords()");
		return lookupForEach ? null : records;
	}
	
	/**
	 * Stores records produced by a thread.
	 * 
	 * @param Records The records to store.
	 */
	protected final synchronized void storeRecords(Map Records) {
		records.putAll(Records);
	}
	
	/**
	 * Executes a recon task for a single instance.
	 * 
	 * @param ITResource The name of the IT resource to execute the task for.
	 * @param ITResourceKey The key of the IT Resource to execute the task for.
	 * @param LookupForEach <code>true</code> indicates a lookup should be created for 
	 *                      each IT resource, <code>false</code> indicates that a single
	 *                      lookup should be used for all values.
	 */
	protected void executeSingleTask(String ITResource, long ITResourceKey, boolean LookupForEach) {
		logger.debug("Entering multipleSourceLookupRecon.executeSingleTask()");
		try {
			Class taskClass = Class.forName(getCriticalAttribute("ReconClass"), true, tcADPClassLoader.getClassLoader());
			OIMReconLookupTaskBase task = (OIMReconLookupTaskBase) taskClass.newInstance();
			logger.debug("Initializing task: " + task.getClass().getName());
			task.setParent(this);
			task.init();
			Properties attributes = getTaskAttributeMap();
			String itResourceProperty = getDefaultAttribute("ITResourceProperty", "ITResourceName");
			attributes.setProperty(itResourceProperty, ITResource);
			if (LookupForEach) {
				String lookup = getCriticalAttribute("Lookup");
				lookup += "." + Long.toString(ITResourceKey);
				attributes.setProperty("Lookup", lookup);
			}
			task.setTaskAttributeMap(attributes);
			try {
				if (LookupForEach) {
					task.execute();
				} else {
					task.openConnection();
					try {
						storeRecords(task.getRecords());
					} finally {
						task.closeConnection();
					}
				}
			} catch (Exception e) {
				if (!LookupForEach) {
					logger.info("Disabling delete reconciliation due to an error on " + ITResource);
					disableDeleteRecon();
					OIMExceptionHandler.handleException(task.logger, e, getClass());
				}
			}
		} catch (Exception e) {
			OIMExceptionHandler.handleException(logger, e, getClass());
		}
		logger.debug("Exiting multipleSourceLookupRecon.executeSingleTask()");
	}
	
	/**
	 * Worker Thread.
	 */
	private class worker extends OIMTaskThreadBase {
		
		/**
		 * The invoking recon task.
		 */
		private OIMMultipleSourceLookupRecon task;
		
		/**
		 * The IT Resource to process.
		 */
		private String itResource;
		
		/**
		 * The key of the IT Resource to process.
		 */
		private long itResourceKey;
		
		/**
		 * <code>true</code> indicates a lookup should be created for each IT resource, <code>false</code> 
		 * indicates that a single lookup should be used for all values.
		 */
		private boolean lookupForEach;
		
		/**
		 * Default Constructor.
		 * 
		 * @param ReconTask The invoking recon task.
		 * @param ITResource The IT Resource to process.
		 * @param ITResourceKey The key of the IT Resource to process.
		 * @param LookupForEach <code>true</code> indicates a lookup should be created for 
		 *                      each IT resource, <code>false</code> indicates that a single
		 *                      lookup should be used for all values.
		 */
		private worker(OIMMultipleSourceLookupRecon Task, String ITResource, long ITResourceKey, boolean LookupForEach) {
			super(Task);
			task = Task;
			itResource = ITResource;
			itResourceKey = ITResourceKey;
			lookupForEach = LookupForEach;
		}
		
		/**
		 * Processes and submits a single recon event.
		 */
		public void runOimThread() {
			task.executeSingleTask(itResource, itResourceKey, lookupForEach);
		}
	}
}